package com.southwind.ioc;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

/**
 * 自定义的ApplicationContext接口实现类
 */
public class ClassPathXmlApplicationContext implements ApplicationContext {

    private static Map<String, Object> ioc = new HashMap<>();

    /**
     * 加载配置文件
     *
     * @param configLocation 配置文件路径
     */
    public ClassPathXmlApplicationContext(String configLocation) {
        if (StringUtils.isEmpty(configLocation)) {
            throw new IllegalArgumentException("configLocation can not be empty");
        }
        // 配置文件路径为绝对路径
        if (StringUtils.contains(configLocation, ":") && !StringUtils.startsWith(configLocation, "classpath:")) {
            SAXReader reader = new SAXReader();
            try {
                Document document = reader.read(configLocation);
                Element rootElement = document.getRootElement();
                Iterator<Element> iterator = rootElement.elementIterator("bean");
                parseBean(ioc, iterator);
            } catch (DocumentException e) {
                e.printStackTrace();
            }
        } /*else if (StringUtils.startsWith(configLocation, "classpath:")) {
            // 配置文件为相对路径
            SAXReader reader = new SAXReader();
            try {
                Document document = reader.read(configLocation);
                Element rootElement = document.getRootElement();
                System.out.println(rootElement);
            } catch (DocumentException e) {
                e.printStackTrace();
            }
        }*/
        // 配置文件为resource目录下文件名
        else {
            SAXReader reader = new SAXReader();
            try {
                InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(configLocation);
                Document document = reader.read(resourceAsStream);
                Element rootElement = document.getRootElement();
                Iterator<Element> iterator = rootElement.elementIterator("bean");
                parseBean(ioc, iterator);
            } catch (DocumentException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 解析bean节点
     *
     * @param ioc ioc容器
     * @param iterator 根节点元素迭代器
     */
    private void parseBean(Map<String, Object> ioc, Iterator iterator) {

            try {
                while (iterator.hasNext()) {
                    Element element = (Element) iterator.next();
                    String id = element.attributeValue("id");
                    String className = element.attributeValue("class");
                    if (StringUtils.isAnyBlank(id, className)) {
                        throw new IllegalArgumentException("id or class can not be empty");
                    }
                    Object bean = Class.forName(className).getConstructor().newInstance();
                    // 创建bean对象，并放入ioc容器中
                    ioc.put(id, bean);
                    // 解析property节点
                    parseProperty(bean, element, ioc, iterator);
                }

            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | InvocationTargetException | NoSuchMethodException | NoSuchFieldException e) {
                e.printStackTrace();
            }

    }

    /**
     * 解析property节点
     *
     * @param beanElement 每个bean节点元素
     * @param rootIterator 根节点元素迭代器
     */
    private void parseProperty(Object bean, Element beanElement, Map<String, Object> ioc, Iterator rootIterator) throws NoSuchFieldException, IllegalAccessException {
        Iterator iterator = beanElement.elementIterator();
        while (iterator.hasNext()) {
            Element propertyElement = (Element) iterator.next();
            String name = propertyElement.attributeValue("name");
            String value = propertyElement.attributeValue("value");
            String ref = propertyElement.attributeValue("ref");
            if (StringUtils.isBlank(name)) {
                throw new IllegalArgumentException("name can not be empty");
            }
            if (StringUtils.isAllBlank(value, ref)) {
                throw new IllegalArgumentException("value or ref can not be empty");
            }
            // 引用类型解析
            if (StringUtils.isNotBlank(ref)) {
                Object o = ioc.get(ref);
                if (Objects.isNull(o)) {
                    parseBean(ioc, rootIterator);
                }
                o = ioc.get(ref);
                if (Objects.nonNull(o)) {
                    Field declaredField = bean.getClass().getDeclaredField(name);
                    declaredField.setAccessible(true);
                    declaredField.set(bean, o);
                } else {
                    throw new IllegalArgumentException("ref bean not found");
                }
            } else {
                // 值类型解析
                Field declaredField = bean.getClass().getDeclaredField(name);
                Class<?> type = declaredField.getType();
                Object setValue = null;
                if (type == long.class || type == Long.class) {
                    setValue = Long.parseLong(value);
                } else if (type == int.class || type == Integer.class) {
                    setValue = Integer.parseInt(value);
                } /*else if (type == boolean.class || type == Boolean.class) {
                            setValue = Boolean.parseBoolean(value);
                        }*/ else if (type == String.class) {
                    setValue = value;
                }
                declaredField.setAccessible(true);
                declaredField.set(bean, setValue);
            }
        }
    }
    /**
     * 根据beanName获取bean对象
     *
     * @param beanName bean的唯一标识
     * @return
     */
    @Override
    public Object getBean(String beanName) {
        if (StringUtils.isBlank(beanName)) {
            throw new IllegalArgumentException("beanName can not be empty");
        }
        return ioc.get(beanName);
    }
}
